// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package com.example.s3;
// snippet-start:[s3.java2.s3_object_operations.complete]

// snippet-start:[s3.java2.s3_object_operations.import]
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Random;
import software.amazon.awssdk.core.waiters.WaiterResponse;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.paginators.ListObjectsV2Iterable;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.model.S3Exception;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.ListObjectsV2Request;
import software.amazon.awssdk.services.s3.model.ListObjectsV2Response;
import software.amazon.awssdk.services.s3.model.S3Object;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
import software.amazon.awssdk.services.s3.model.DeleteBucketRequest;
import software.amazon.awssdk.services.s3.model.CreateMultipartUploadRequest;
import software.amazon.awssdk.services.s3.model.CreateMultipartUploadResponse;
import software.amazon.awssdk.services.s3.model.CompletedMultipartUpload;
import software.amazon.awssdk.services.s3.model.CreateBucketRequest;
import software.amazon.awssdk.services.s3.model.CompletedPart;
import software.amazon.awssdk.services.s3.model.CreateBucketConfiguration;
import software.amazon.awssdk.services.s3.model.UploadPartRequest;
import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadRequest;
import software.amazon.awssdk.services.s3.waiters.S3Waiter;
import software.amazon.awssdk.services.s3.model.HeadBucketRequest;
import software.amazon.awssdk.services.s3.model.HeadBucketResponse;
// snippet-end:[s3.java2.s3_object_operations.import]

// snippet-start:[s3.java2.s3_object_operations.main]

/**
 * To run this AWS code example, ensure that you have setup your development
 * environment, including your AWS credentials.
 *
 * For information, see this documentation topic:
 *
 * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
 */
public class S3ObjectOperations {
        private static S3Client s3;

        public static void main(String[] args) throws IOException {
                final String USAGE = """

                                Usage:
                                    <bucketName> <key>

                                Where:
                                    bucketName - the Amazon S3 bucket to create.
                                    key - the key to use.
                                """;

                if (args.length != 2) {
                        System.out.println(USAGE);
                        System.exit(1);
                }

                String bucketName = args[0];
                String key = args[1];

                // snippet-start:[s3.java2.s3_object_operations.upload]
                Region region = Region.US_WEST_2;
                s3 = S3Client.builder()
                                .region(region)
                                .build();

                createBucket(s3, bucketName, region);

                PutObjectRequest objectRequest = PutObjectRequest.builder()
                                .bucket(bucketName)
                                .key(key)
                                .build();

                s3.putObject(objectRequest, RequestBody.fromByteBuffer(getRandomByteBuffer(10_000)));
                // snippet-end:[s3.java2.s3_object_operations.upload]

                // Multipart upload example
                String multipartKey = "multiPartKey";
                multipartUpload(bucketName, multipartKey);

                // snippet-start:[s3.java2.s3_object_operations.pagination]
                ListObjectsV2Request listObjectsReqManual = ListObjectsV2Request.builder()
                                .bucket(bucketName)
                                .maxKeys(1)
                                .build();

                boolean done = false;
                while (!done) {
                        ListObjectsV2Response listObjResponse = s3.listObjectsV2(listObjectsReqManual);
                        for (S3Object content : listObjResponse.contents()) {
                                System.out.println(content.key());
                        }

                        if (listObjResponse.nextContinuationToken() == null) {
                                done = true;
                        }

                        listObjectsReqManual = listObjectsReqManual.toBuilder()
                                        .continuationToken(listObjResponse.nextContinuationToken())
                                        .build();
                }
                // snippet-end:[s3.java2.s3_object_operations.pagination]
                // snippet-start:[s3.java2.s3_object_operations.iterative]
                ListObjectsV2Request listReq = ListObjectsV2Request.builder()
                                .bucket(bucketName)
                                .maxKeys(1)
                                .build();

                ListObjectsV2Iterable listRes = s3.listObjectsV2Paginator(listReq);
                // Process response pages
                listRes.stream()
                                .flatMap(r -> r.contents().stream())
                                .forEach(content -> System.out
                                                .println(" Key: " + content.key() + " size = " + content.size()));

                // snippet-end:[s3.java2.s3_object_operations.iterative]
                // snippet-start:[s3.java2.s3_object_operations.stream]
                // Helper method to work with paginated collection of items directly.
                listRes.contents().stream()
                                .forEach(content -> System.out
                                                .println(" Key: " + content.key() + " size = " + content.size()));

                // snippet-end:[s3.java2.s3_object_operations.stream]
                // snippet-start:[s3.java2.s3_object_operations.forloop]
                for (S3Object content : listRes.contents()) {
                        System.out.println(" Key: " + content.key() + " size = " + content.size());
                }
                // snippet-end:[s3.java2.s3_object_operations.forloop]

                // snippet-start:[s3.java2.s3_object_operations.download]
                GetObjectRequest getObjectRequest = GetObjectRequest.builder()
                                .bucket(bucketName)
                                .key(key)
                                .build();

                s3.getObject(getObjectRequest);
                // snippet-end:[s3.java2.s3_object_operations.download]

                // snippet-start:[s3.java2.s3_object_operations.delete]
                DeleteObjectRequest deleteObjectRequest = DeleteObjectRequest.builder()
                                .bucket(bucketName)
                                .key(key)
                                .build();

                s3.deleteObject(deleteObjectRequest);
                // snippet-end:[s3.java2.s3_object_operations.delete]

                // Delete an object
                deleteObjectRequest = DeleteObjectRequest.builder()
                                .bucket(bucketName)
                                .key(multipartKey)
                                .build();

                s3.deleteObject(deleteObjectRequest);
                deleteBucket(s3, bucketName);
                System.out.println("Done");
        }

        // Create a bucket by using a S3Waiter object
        public static void createBucket(S3Client s3Client, String bucketName, Region region) {

                S3Waiter s3Waiter = s3Client.waiter();

                try {
                        CreateBucketRequest bucketRequest = CreateBucketRequest.builder()
                                        .bucket(bucketName)
                                        .createBucketConfiguration(
                                                        CreateBucketConfiguration.builder()
                                                                        .locationConstraint(region.id())
                                                                        .build())
                                        .build();

                        s3Client.createBucket(bucketRequest);
                        HeadBucketRequest bucketRequestWait = HeadBucketRequest.builder()
                                        .bucket(bucketName)
                                        .build();

                        // Wait until the bucket is created and print out the response
                        WaiterResponse<HeadBucketResponse> waiterResponse = s3Waiter
                                        .waitUntilBucketExists(bucketRequestWait);
                        waiterResponse.matched().response().ifPresent(System.out::println);
                        System.out.println(bucketName + " is ready");

                } catch (S3Exception e) {
                        System.err.println(e.awsErrorDetails().errorMessage());
                        System.exit(1);
                }
        }

        public static void deleteBucket(S3Client client, String bucket) {
                DeleteBucketRequest deleteBucketRequest = DeleteBucketRequest.builder()
                                .bucket(bucket)
                                .build();
                client.deleteBucket(deleteBucketRequest);
        }

        /**
         * Upload an object in parts
         */
        private static void multipartUpload(String bucketName, String key) throws IOException {

                int mB = 1024 * 1024;
                // snippet-start:[s3.java2.s3_object_operations.upload_multi_part]
                // First create a multipart upload and get the upload id
                CreateMultipartUploadRequest createMultipartUploadRequest = CreateMultipartUploadRequest.builder()
                                .bucket(bucketName)
                                .key(key)
                                .build();

                CreateMultipartUploadResponse response = s3.createMultipartUpload(createMultipartUploadRequest);
                String uploadId = response.uploadId();
                System.out.println(uploadId);

                // Upload all the different parts of the object
                UploadPartRequest uploadPartRequest1 = UploadPartRequest.builder()
                                .bucket(bucketName)
                                .key(key)
                                .uploadId(uploadId)
                                .partNumber(1).build();

                String etag1 = s3
                                .uploadPart(uploadPartRequest1, RequestBody.fromByteBuffer(getRandomByteBuffer(5 * mB)))
                                .eTag();

                CompletedPart part1 = CompletedPart.builder().partNumber(1).eTag(etag1).build();

                UploadPartRequest uploadPartRequest2 = UploadPartRequest.builder().bucket(bucketName).key(key)
                                .uploadId(uploadId)
                                .partNumber(2).build();
                String etag2 = s3
                                .uploadPart(uploadPartRequest2, RequestBody.fromByteBuffer(getRandomByteBuffer(3 * mB)))
                                .eTag();
                CompletedPart part2 = CompletedPart.builder().partNumber(2).eTag(etag2).build();

                // Finally call completeMultipartUpload operation to tell S3 to merge all
                // uploaded
                // parts and finish the multipart operation.
                CompletedMultipartUpload completedMultipartUpload = CompletedMultipartUpload.builder()
                                .parts(part1, part2)
                                .build();

                CompleteMultipartUploadRequest completeMultipartUploadRequest = CompleteMultipartUploadRequest.builder()
                                .bucket(bucketName)
                                .key(key)
                                .uploadId(uploadId)
                                .multipartUpload(completedMultipartUpload)
                                .build();

                s3.completeMultipartUpload(completeMultipartUploadRequest);
                // snippet-end:[s3.java2.s3_object_operations.upload_multi_part]
        }

        private static ByteBuffer getRandomByteBuffer(int size) throws IOException {
                byte[] b = new byte[size];
                new Random().nextBytes(b);
                return ByteBuffer.wrap(b);
        }
}

// snippet-end:[s3.java2.s3_object_operations.main]
// snippet-end:[s3.java2.s3_object_operations.complete]